﻿
// ****************************************************************
// 
// Copyright © 2013
// Author:Neil
// Creation Time:2013/6/7 23:22:39
// 
// File Name:ObjectExtensions.cs
// File Description:
// 
// ****************************************************************


using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Kunlun.Common
{
    /// <summary>
    /// 针对所有Object的扩展函数
    /// </summary>
    public static class ObjectExtensions
    {
        /// <summary>
        /// 判断给定的对象是否为空
        /// </summary>
        /// <param name="obj">待判断的对象</param>
        /// <returns>返回true，表示给定的对象为null</returns>
        public static bool IsNull(this object obj)
        {
            return (obj == null);
        }

        /// <summary>
        /// 判断给定的对象是否有值
        /// </summary>
        /// <param name="obj">待判断的对象</param>
        /// <returns>返回false，表示给定的对象为null</returns>
        public static bool HasValue(this object obj)
        {
            return (obj != null);
        }

        /// <summary>
        /// 判断给定的对象中是否存在指定的属性
        /// </summary>
        /// <param name="obj">待检索的对象</param>
        /// <param name="propertyName">待检索的属性名</param>
        /// <returns>当返回true时，表示给定的对象obj中存在指定的属性</returns>
        public static bool HasProperty(this object obj, String propertyName)
        {
            if (obj.IsNull())
                return false;

            var type = obj.GetType();
            // 从type中搜索指定名称的属性
            var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.IgnoreCase);

            return property.HasValue();
        }

        /// <summary>
        /// 从对象中获取指定属性的值
        /// </summary>
        /// <typeparam name="T">属性值类型</typeparam>
        /// <param name="obj">源对象</param>
        /// <param name="propertyName">待读取的属性</param>
        /// <returns>返回已经转换过类型的属性值</returns>
        public static T GetProperty<T>(this object obj, String propertyName)
        {
            var result = GetProperty(obj, propertyName, false);

            return (T)result;
        }

        /// <summary>
        /// 从对象指定属性获取值
        /// </summary>
        /// <param name="obj">源对象</param>
        /// <param name="propertyName">属性名</param>
        /// <param name="throwException">如果不存在此属性或属性不可读，是否抛出异常</param>
        /// <returns>返回属性值</returns>
        /// <exception cref="PropertyNotExistsException">当对象中不存在指定属性，且throwException=true时，抛出此异常</exception>
        public static Object GetProperty(this object obj, String propertyName, bool throwException = false)
        {
            if (obj.IsNull())
            {
                if (throwException)
                    throw new ArgumentNullException("obj");
                return null;
            }

            // 获取对象的类型
            var type = obj.GetType();

            // 获取属性信息
            var propertyInfo = type.GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static);

            if (propertyInfo.IsNull())
            {
                if (throwException)
                    throw new PropertyNotExistsException(type, propertyName);
                return null;
            }

            if (!propertyInfo.CanRead)
            {
                if (throwException)
                    throw new PropertyAccessException(type, propertyName);
                return null;
            }

            // 返回属性值
            return propertyInfo.GetValue(obj, null);
        }
    }

    /// <summary>
    /// 类型中不存在指定属性异常
    /// </summary>
    public class PropertyNotExistsException : Exception
    {
        public PropertyNotExistsException(Type type, string propertyName)
            : base(String.Format("类型 {0} 中不存在名为 {1} 的属性。", type.FullName, propertyName))
        {
        }
    }

    /// <summary>
    /// 当类型的指定属性不可读时触发此异常
    /// </summary>
    public class PropertyAccessException : Exception
    {
        public PropertyAccessException(Type type, string propertyName)
            : base(String.Format("类型 {0} 中的属性 {1} 被设置为不可读。", type.FullName, propertyName))
        {
        }
    }
}



